'use strict';

const RouterService = require('../base');
const config = require("../../../config/wx");
const path = require("path");
const fs = require("fs");
const crypto = require("crypto");
const { Op } = require("sequelize");
const { BalanceLogTypeEnum, WxTransferStatusEnum } = require("../../utils/enum");

function generateNonceStr(len) {
    let data = "ABCDEFGHJKMNPQRSTWXYZabcdefhijkmnprstwxyz2345678";
    let str = "";
    for (let i = 0; i < len; i++) {
        str += data.charAt(Math.floor(Math.random() * data.length));
    }
    return str;
}

function generateWithdrawNo() {
    const now = new Date();
    const year = now.getFullYear();
    const month = String(now.getMonth() + 1).padStart(2, '0');
    const day = String(now.getDate()).padStart(2, '0');
    const timestamp = String(now.getTime()).slice(-6);
    const random = Math.floor(Math.random() * 1000).toString().padStart(3, '0');
    return `WD${year}${month}${day}${timestamp}${random}`;
}

class BalanceRecordService extends RouterService {
    static get modelName() {
        return 'Business.BalanceRecord';
    }
    getPageInclude() {
        return [
            {
                model: this.ctx.model.Business.User,
                as: 'user'
            }
        ]
    }
    /**
     * 创建提现
     */
    async withdraw(data) {
        const { user_id, amount } = data;

        // 验证参数
        if (!user_id || !amount) {
            this.ctx.throw(400, '缺少必要参数');
        }

        if (amount < 0.1) {
            this.ctx.throw(400, '提现金额不能少于0.1元');
        }

        // 用户判空
        const user = await this.ctx.model.Business.User.findByPk(user_id);
        if (!user) {
            this.ctx.throw(400, '用户不存在');
        }
        if (!user.openid) {
            this.ctx.throw(400, '用户未绑定微信');
        }

        // 验证余额
        if (parseFloat(user.balance) < amount) {
            this.ctx.throw(400, '余额不足');
        }

        // 验证每日提现限额
        const todayTotal = await this.getTodayWithdrawTotal(user_id);
        if (todayTotal + amount > 2000) {
            this.ctx.throw(400, '超出每日提现限额2000元');
        }

        // 生成提现单号
        const withdrawNo = generateWithdrawNo();
        // 查询手续费比例
        const feeRatioConfig = await this.ctx.model.Business.Config.findOne({
            where: {
                key: 'fee_ratio'
            }
        })
        let feeRatio = 0
        if (feeRatioConfig) {
            feeRatio = parseFloat(feeRatioConfig.value || 0)
        }
        // 3. 调用微信提现接口
        const transferResult = await this.callWxTransfer({
            mchId: config.mch_id,
            appId: config.appid,
            out_bill_no: withdrawNo,
            openid: user.openid,
            transfer_amount: amount,
            transfer_remark: '佣金提现'
        });
        if (transferResult.state === WxTransferStatusEnum.WAIT_USER_CONFIRM || transferResult.state === WxTransferStatusEnum.TRANSFERING) {
            
            // 创建事务
            const transaction = await this.ctx.model.transaction();
            try {
                // 1. 先扣减用户余额
                await this.ctx.model.Business.User.update({
                    balance: this.ctx.model.Sequelize.literal(`balance - ${amount}`)
                }, {
                    where: { id: user_id },
                    transaction
                });
                const fee = amount * feeRatio > 0.01 ? amount * feeRatio : 0.01
                // 2. 创建提现记录
                const res = await this.ctx.model.Business.BalanceRecord.create({
                    user_id: user_id,
                    user_type: user.user_type,
                    amount: -amount, // 负数表示提现
                    amount2: -fee, // 负数表示手续费
                    type: BalanceLogTypeEnum.WITHDRAW,
                    description: `佣金提现`,
                    status: 0,
                    out_bill_no: withdrawNo,
                    transfer_bill_no: transferResult.transfer_bill_no,
                    create_time: transferResult.create_time
                }, { transaction });

                // 4. 如果微信调用成功，提交事务
                await transaction.commit();

                // 返回小程序需要的数据格式
                return {
                    id: res.id,
                    mchId: config.mch_id,
                    appId: config.appid,
                    package: transferResult.package_info,
                    withdraw_no: withdrawNo,
                    amount: amount,
                    status: '提现申请已提交'
                }

            } catch (error) {
                // 回滚事务
                await transaction.rollback();
                this.ctx.logger.error('提现失败:', error);
                throw new Error(error.message || '提现申请失败');
            }
        } else {
            this.ctx.throw(400, '提现失败' + transferResult.state);
        }

    }

    /**
     * 重新发起提现
     */
    async retryWithdraw(data) {
        const { id } = data;
        const balanceRecord = await this.ctx.model.Business.BalanceRecord.findByPk(id);
        const user = await this.ctx.model.Business.User.findByPk(balanceRecord.user_id);
        if (!balanceRecord) {
            this.ctx.throw(400, '提现订单不存在');
        }
        const transferResult = await this.callWxTransfer({
            mchId: config.mch_id,
            appId: config.appid,
            out_bill_no: balanceRecord.out_bill_no,
            openid: user.openid,
            transfer_amount: balanceRecord.amount,
            transfer_remark: '佣金提现'
        });
        this.ctx.logger.error('重新发起提现', {
            transferResult
        });
        
        if (transferResult.state === WxTransferStatusEnum.WAIT_USER_CONFIRM || transferResult.state === WxTransferStatusEnum.TRANSFERING || transferResult.state === WxTransferStatusEnum.SUCCESS) {
            throw new Error('提现受理中，请耐心等待');
        } else {
            return {
                id: balanceRecord.id,
                mchId: config.mch_id,
                appId: config.appid,
                package: transferResult.package_info,
                withdraw_no: balanceRecord.out_bill_no,
                amount: balanceRecord.amount,
                status: '提现申请已提交'
            }// 更新提现记录，加回余额
            const transaction = await this.ctx.model.transaction();
            try {
                await this.ctx.model.Business.User.update({
                    balance: this.ctx.model.Sequelize.literal(`balance + ${balanceRecord.amount}`)
                }, {
                    where: { id: balanceRecord.user_id },
                    transaction
                });
                // 更新提现记录状态
                await this.ctx.model.Business.BalanceRecord.update({
                    status: -1,
                    state: transferResult.state,
                    fail_reason: transferResult.fail_reason,
                }, {
                    where: { id: balanceRecord.id },
                    transaction
                });
                await transaction.commit();
            } catch (error) {
                await transaction.rollback();
                this.ctx.logger.error('提现失败:', error);
                throw new Error(error.message || '提现申请失败');
            }
            this.ctx.throw(400, '提现失败，请重新发起提现');
        }
    }

    /**
     * 调用微信提现接口
     */
    async callWxTransfer(params) {
        const { out_bill_no, openid, transfer_amount, transfer_remark } = params;

        const transferParams = {
            appid: config.appid,
            out_bill_no: out_bill_no,
            transfer_scene_id: "1005", // 1000-服务商代发场景 用户提现佣金
            openid: openid,
            transfer_amount: Math.ceil(Math.abs(transfer_amount) * 100),
            transfer_remark: transfer_remark,
            notify_url: config.notify_url_transfer, // 提现回调地址
            user_recv_perception: "劳务报酬",
            transfer_scene_report_infos: [
                {
                    "info_type": "岗位类型",
                    "info_content": "经纪人"
                },
                {
                    "info_type": "报酬说明",
                    "info_content": "游客订单提成"
                }
            ]
        };

        const timeStamp = Math.floor(new Date().getTime() / 1000);
        const nonceStr = generateNonceStr(32);

        const Authorization = `WECHATPAY2-SHA256-RSA2048 mchid="${config.mch_id}",nonce_str="${nonceStr}",signature="${this.createTransferSign('POST',
            '/v3/fund-app/mch-transfer/transfer-bills',
            timeStamp,
            nonceStr,
            transferParams
        )}",timestamp="${timeStamp}",serial_no="${config.serial_no}"`;

        try {
            const transferRes = await this.ctx.curl('https://api.mch.weixin.qq.com/v3/fund-app/mch-transfer/transfer-bills', {
                method: "POST",
                data: transferParams,
                headers: {
                    Authorization: Authorization,
                    Accept: 'application/json',
                    'Content-Type': 'application/json',
                    'Wechatpay-Serial': config.serial_no
                },
                dataType: 'json'
            });
            console.log(transferRes)
            if (transferRes.status === 200) {
                return transferRes.data;
            } else {
                throw new Error(transferRes.data.message || '微信提现接口调用失败');
            }
        } catch (error) {
            this.ctx.logger.error('微信提现接口调用失败:', error, transferParams);
            throw new Error('微信提现接口调用失败: ' + (error.message || '网络错误'));
        }
    }

    /**
     * 生成微信提现签名
     */
    createTransferSign(method, url, timestamp, nonce_str, params) {
        // 签名串
        let signStr = `${method}\n${url}\n${timestamp}\n${nonce_str}\n${JSON.stringify(params)}\n`;

        // 读取API证书文件内容
        const filepath = path.join('app', '/public/apiclient_key.pem');
        let cert = fs.readFileSync(filepath, "utf-8");

        // 创建使用 RSA 算法和 SHA-256 散列算法的签名对象
        let sign = crypto.createSign("RSA-SHA256");
        sign.update(signStr);
        return sign.sign(cert, "base64");
    }


    /**
     * 获取用户今日提现总金额
     */
    async getTodayWithdrawTotal(user_id) {
        const today = new Date();
        const startOfDay = new Date(today.getFullYear(), today.getMonth(), today.getDate());
        const endOfDay = new Date(today.getFullYear(), today.getMonth(), today.getDate() + 1);

        const result = await this.ctx.model.Business.BalanceRecord.findOne({
            where: {
                user_id: user_id,
                type: BalanceLogTypeEnum.WITHDRAW,
                create_time: {
                    [Op.gte]: startOfDay,
                    [Op.lt]: endOfDay
                },
                status: {
                    // 1、0、-1
                    [Op.or]: [1, 0, -1]
                }
            },
            attributes: [
                [this.ctx.model.Sequelize.fn('SUM', this.ctx.model.Sequelize.fn('ABS', this.ctx.model.Sequelize.col('amount'))), 'total']
            ],
            raw: true
        });
        return result.total || 0
    }

    /**
     * 扣除手续费
     */
    async deductFee(out_bill_no) {
        // 手续费记录
        const feeRecord = await this.ctx.model.Business.BalanceRecord.findOne({
            where: {
                out_bill_no: out_bill_no,
                type: BalanceLogTypeEnum.WITHDRAW_FEE
            }
        });
        if (feeRecord) {
            throw new Error('手续费记录已存在');
            return
        }
        const balanceRecord = await this.ctx.model.Business.BalanceRecord.findOne({
            where: {
                out_bill_no: out_bill_no,
                type: BalanceLogTypeEnum.WITHDRAW
            }
        });
        if (!balanceRecord) {
            throw new Error('提现订单不存在');
            return
        }
        // 查询配置表中的手续费比例
        const feeRatioConfig = await this.ctx.model.Business.Config.findOne({
            where: {
                key: 'fee_ratio'
            }
        })
        let feeRatio = 0
        if (feeRatioConfig) {
            feeRatio = parseFloat(feeRatioConfig.value || 0)
        }
        if (feeRatio === 0) return true
        // 增加手续费记录和扣款
        // 计算手续费金额
        const fee = balanceRecord.amount * feeRatio > 0.01 ? balanceRecord.amount * feeRatio : 0.01
        await this.ctx.model.Business.User.update({
            balance: this.ctx.model.Sequelize.literal(`balance - ${fee}`)
        }, {
            where: { id: balanceRecord.user_id }
        });
        await this.ctx.model.Business.BalanceRecord.create({
            out_bill_no: out_bill_no,
            user_id: balanceRecord.user_id,
            amount: -fee,
            type: BalanceLogTypeEnum.WITHDRAW_FEE,
            description: `佣金提现手续费`,
        });
        return true
    }
    /**
     * 充值
     */
    async createRecharge({ user_id, amount }) {
        if (!user_id) {
            throw new Error('用户不存在');
        }
        if (isNaN(Number(amount))) {
            throw new Error('充值金额不合法');
        }

        if (Number(amount) < 0.1) {
            this.ctx.throw(400, '充值金额不能少于0.1元');
        }

        const user = await this.ctx.model.Business.User.findByPk(user_id);
        if (!user) {
            this.ctx.throw(400, '用户不存在');
        }

        // 创建事务
        const transaction = await this.ctx.model.transaction();
        try {
            // 1. 先增加用户余额
            await this.ctx.model.Business.User.update({
                balance: this.ctx.model.Sequelize.literal(`balance + ${amount}`)
            }, {
                where: { id: user_id },
                transaction
            });
            const rechargeNo = generateWithdrawNo();
            // 2. 创建充值记录
            await this.ctx.model.Business.BalanceRecord.create({
                order_no: rechargeNo,
                user_id: user_id,
                amount: amount,
                type: BalanceLogTypeEnum.ADMIN_RECHARGE,
                description: `后台充值`,
            }, { transaction });
            console.log('提交事务')
            // 2. 提交事务
            await transaction.commit();

            return '充值成功'
        } catch (error) {
            await transaction.rollback();
            console.log('回滚事务', error)
            throw new Error(error.message || '充值失败');
        }
    }
}

module.exports = BalanceRecordService;
